// Copyright (c) 2022 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef REDISTRIBUTE_DISTRIBUTED_BEADING_STRATEGY_H
#define REDISTRIBUTE_DISTRIBUTED_BEADING_STRATEGY_H

#include "../settings/types/Ratio.h"
#include "BeadingStrategy.h"

namespace cura
{
/*!
 * A meta-beading-strategy that takes outer and inner wall widths into account.
 *
 * The outer wall will try to keep a constant width by only applying the beading strategy on the inner walls. This
 * ensures that this outer wall doesn't react to changes happening to inner walls. It will limit print artifacts on
 * the surface of the print. Although this strategy technically deviates from the original philosophy of the paper.
 * It will generally results in better prints because of a smoother motion and less variation in extrusion width in
 * the outer walls.
 *
 * If the thickness of the model is less then two times the optimal outer wall width and once the minimum inner wall
 * width it will keep the minimum inner wall at a minimum constant and vary the outer wall widths symmetrical. Until
 * The thickness of the model is that of at least twice the optimal outer wall width it will then use two
 * symmetrical outer walls only. Until it transitions into a single outer wall. These last scenario's are always
 * symmetrical in nature, disregarding the user specified strategy.
 */
class RedistributeBeadingStrategy : public BeadingStrategy
{
public:
    /*!
     * /param optimal_width_outer         Outer wall width, guaranteed to be the actual (save rounding errors) at a
     *                                    bead count if the parent strategies' optimum bead width is a weighted
     *                                    average of the outer and inner walls at that bead count.
     * /param minimum_variable_line_ratio Minimum factor that the variable line might deviate from the optimal width.
     */
    RedistributeBeadingStrategy(const coord_t optimal_width_outer, const Ratio minimum_variable_line_ratio, BeadingStrategyPtr parent);

    virtual ~RedistributeBeadingStrategy() override = default;

    Beading compute(coord_t thickness, coord_t bead_count) const override;

    coord_t getOptimalThickness(coord_t bead_count) const override;
    coord_t getTransitionThickness(coord_t lower_bead_count) const override;
    coord_t getOptimalBeadCount(coord_t thickness) const override;
    coord_t getTransitioningLength(coord_t lower_bead_count) const override;
    double getTransitionAnchorPos(coord_t lower_bead_count) const override;

    std::string toString() const override;

protected:
    /*!
     * Determine the outer bead width.
     *
     * According to the following logic:
     *  - If the thickness of the model is more then twice the optimal outer bead width and the minimum inner bead
     *  width it will return the optimal outer bead width.
     *  - If the thickness is less then twice the optimal outer bead width and the minimum inner bead width, but
     *  more them twice the optimal outer bead with it will return the optimal bead width minus half the inner bead
     *  width.
     *  - If the thickness is less then twice the optimal  outer bead width it will return half the thickness as
     *  outer bead width
     *
     * \param thickness Thickness of the total beads.
     * \param optimal_width_outer User specified optimal outer bead width.
     * \param minimum_width_inner Inner bead width times the minimum variable line width.
     * \return The outer bead width.
     */
    static coord_t getOptimalOuterBeadWidth(coord_t thickness, coord_t optimal_width_outer, coord_t minimum_width_inner);

    /*!
     * Moves the beads towards the outer edges of thickness and ensures that the outer walls are locked in location
     * \param beading The beading instance.
     * \param thickness The thickness of the bead.
     */
    static void resetToolPathLocations(Beading& beading, coord_t thickness);

    /*!
     * Filters and validates the beads, to ensure that all inner beads are at least the minimum bead width.
     *
     * \param beading The beading instance.
     * \param minimum_width_inner Inner bead width times the minimum variable line width.
     * \return true if beads are removed.
     */
    static bool validateInnerBeadWidths(Beading& beading, coord_t minimum_width_inner);

    BeadingStrategyPtr parent_;
    coord_t optimal_width_outer_;
    Ratio minimum_variable_line_ratio_;
};

} // namespace cura
#endif // INWARD_DISTRIBUTED_BEADING_STRATEGY_H
